#ifndef xpack_chrono_datetime
#define xpack_chrono_datetime
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::chrono_datetime::inc
#include"chrono/date.hpp"
#include"chrono/time.hpp"
#include"chrono/day.hpp"
#include"define/classify.hpp"
#include"macro/xexport.hpp"
#include"macro/xstruct.hpp"
#include"memop/cmp.hpp"
#include"memop/zeros.hpp"
#include"meta/is_the_xstruct.hpp"
#pragma pop_macro("xuser")

namespace mixc::chrono_datetime::origin{
    template<class field_date = u32, class field_time = field_date>
    xstruct(
        xtmpl(datetimex, field_date, field_time),
        xpubb(inc::timex<field_time>),    // 低位
        xpubb(inc::datex<field_date>),    // 高位 先比较
        xwhos(inc::classify_t::chrono_datetime)
    )
    private:
        using the_time      = inc::timex<field_time>;
        using the_date      = inc::datex<field_date>;
        using the_time_ref  = the_time &;
        using the_date_ref  = the_date &;

    public:
        xprops()
            xpubgetx(is_valid_date, bool){
                xr{ return the_date_ref(xthe)->is_valid; }
            };

            xpubgetx(is_valid_12h_time, bool){
                xr{ 
                    auto & base = the_time_ref(xthe);
                    bool is_valid_12h_time = base->is_valid_12h_time;
                    return is_valid_12h_time;
                }
            };

            xpubgetx(is_valid_24h_time, bool){
                xr{ 
                    auto & base = the_time_ref(xthe);
                    bool is_valid_24h_time = base->is_valid_24h_time;
                    return is_valid_24h_time; 
                }
            };

            xpubgetx(is_valid_time, bool){
                xr{ return xthe->is_valid_12h_time or xthe->is_valid_24h_time; }
            };

            xpubgetx(is_valid_time_when_allow_leap_second, bool){
                xr{
                    return 
                        xthe->is_valid_12h_time_when_allow_leap_second or 
                        xthe->is_valid_24h_time_when_allow_leap_second;
                }
            };

            xpubgetx(is_valid_12h_time_when_allow_leap_second, bool){
                xr{ 
                    auto & base = the_time_ref(xthe);
                    bool is_valid_12h_time = base->is_valid_12h_time_when_allow_leap_second;
                    return is_valid_12h_time;
                }
            };

            xpubgetx(is_valid_24h_time_when_allow_leap_second, bool){
                xr{ 
                    auto & base = the_time_ref(xthe);
                    bool is_valid_24h_time = base->is_valid_24h_time_when_allow_leap_second;
                    return is_valid_24h_time; 
                }
            };



            xpubgetx(is_valid, bool){
                xr{ return xthe->is_valid_date and xthe->is_valid_time; }
            };

            xpubgetx(is_valid_12h_datetime, bool){
                xr{ return xthe->is_valid_date and xthe->is_valid_12h_time; }
            };

            xpubgetx(is_valid_24h_datetime, bool){
                xr{ return xthe->is_valid_date and xthe->is_valid_24h_time; }
            };


            xpubgetx(is_valid_when_allow_leap_second, bool){
                xr{ return xthe->is_valid_date and xthe->is_valid_time_when_allow_leap_second; }
            };

            xpubgetx(is_valid_12h_datetime_when_allow_leap_second, bool){
                xr{ return xthe->is_valid_date and xthe->is_valid_12h_time_when_allow_leap_second; }
            };

            xpubgetx(is_valid_24h_datetime_when_allow_leap_second, bool){
                xr{ return xthe->is_valid_date and xthe->is_valid_24h_time_when_allow_leap_second; }
            };

            xpubget_pubsetx(total_milisecond, u64){
                xr{
                    constexpr u64 one_day = 24 * 3600 * 1000;
                    auto & base = the_time_ref(xthe);
                    u64 part_a  = base->total_milisecond;
                    u64 part_b  = one_day * inc::day(xthe);
                    u64 ms      = part_a + part_b;
                    return ms;
                }
                xw{
                    constexpr u64 one_day = 24 * 3600 * 1000;
                    auto & base = the_time_ref(xthe);

                    // 先设置，会将 time 域的数据清 0
                    xthe        = inc::day(value / one_day);

                    // 后设置
                    base->total_milisecond = value % one_day;
                }
            };

            xpubgetx(yesterday, the_t){
                xr{
                    auto base_date = the_date_ref(xthe)->yesterday();
                    auto & base_time = the_time_ref(xthe);
                    return the_t(base_date, base_time);
                }
            };

            xpubgetx(tomorrow, the_t){
                xr{
                    auto base_date = the_date_ref(xthe)->tomorrow();
                    auto & base_time = the_time_ref(xthe);
                    return the_t(base_date, base_time);
                }
            };
        $

        xprops_operator()

    public:
        template<inc::is_nowxx now_t>
        constexpr datetimex(now_t):
            datetimex(now_t::datetime()){
        }

        constexpr datetimex(){
            // 由于使用了 inc::cmp_des 二进制比较，所以需要保证结构体不存在未初始化的位域
            inc::zeros(this, sizeof(the_t));
        }

        template<inc::is_datetime datetime_t>
        constexpr datetimex(datetime_t const & object):
            datetimex(
                object->year,
                object->month,
                object->day,
                object->hour,
                object->minute,
                object->second,
                object->millisecond
            ){
        }

        constexpr datetimex(
            field_date year, 
            field_date month       = 0, 
            field_date day         = 0, 
            field_time hour        = 0, 
            field_time minute      = 0, 
            field_time second      = 0, 
            field_time millisecond = 0
        ) : datetimex(){
            the_time_ref(xthe) = the_time(hour, minute, second, millisecond);
            the_date_ref(xthe) = the_date(year, month, day);
        }

        template<inc::is_date date_t, inc::is_time time_t>
        constexpr datetimex(date_t const & date, time_t const & time): 
            the_time(time), the_date(date){
        }

        ixx compare(the_t const & value) const {
            auto date_left          = the_date(xthe);
            auto date_right         = the_date(value);
            auto result             = date_left.compare(date_right);

            // 如果日期相等，就比较时间
            if (result == 0){
                auto time_left      = the_time(xthe);
                auto time_right     = the_time(value);
                result              = time_left.compare(time_right);
            }
            return result;
        }

        friend ixx operator- (the_t const & left, the_t const & right) {
            return (ixx)(left->total_milisecond - right->total_milisecond);
        }

        friend the_t operator- (the_t const & left, inc::day right) {
            the_t r             = (the_t &)left;
            (the_date &)r      -= right;
            return r;
        }

        friend the_t operator-= (the_t & left, inc::day right) {
            left = left - right;
            return left;
        }

        friend the_t operator+ (the_t const & left, inc::day right) {
            the_t r             = (the_t &)left;
            (the_date &)r  += right;
            return r;
        }

        friend the_t operator+= (the_t & left, inc::day right) {
            left                = left + right;
            return left;
        }

        #define xa_args_list       the_t const & left, the_t const & right
        #define xa_invoke          left.compare(right)
        #define xa_is_friend
        #include"macro/xgen.cmp.hpp"
    $

    using datetime = datetimex<>;
}

#endif

xexport_space(mixc::chrono_datetime::origin)
